#include <iostream>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <fstream>
#include <math.h>
#include <cstdlib>
#include <unistd.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>

#include <termios.h>
#include <unistd.h>
#include <fcntl.h>
 

#include <vector>


using namespace std;
#include "asteroid.cpp"
#include "asteroidpelem.cpp"
#include "asteroidwithsize.cpp"
#include "asteroidwithtax.cpp"
#include "asteroidwithpole.cpp"
#include "masiero.cpp"
#include "nugent.cpp"

#define GPFIFO "./gpio" /* any unique name */

int kbhit(void)
{
  struct termios oldt, newt;
  int ch;
  int oldf;
 
  tcgetattr(STDIN_FILENO, &oldt);
  newt = oldt;
  newt.c_lflag &= ~(ICANON | ECHO);
  tcsetattr(STDIN_FILENO, TCSANOW, &newt);
  oldf = fcntl(STDIN_FILENO, F_GETFL, 0);
  fcntl(STDIN_FILENO, F_SETFL, oldf | O_NONBLOCK);
 
  ch = getchar();

  //  cout << "passed" << endl;
  tcsetattr(STDIN_FILENO, TCSANOW, &oldt);
  fcntl(STDIN_FILENO, F_SETFL, oldf);
 
  if(ch != EOF)
  {
    ungetc(ch, stdin);
    return 1;
  }
 
  return 0;
}

/*
AsteroidWithSize* bin2ast(long *N)
{
  ifstream fin("ast.bin");
  fin.read((char*)N,sizeof(*N));

  AsteroidWithSize *myAst = new AsteroidWithSize[*N];

  for(long i=0; i<*N; i++)
    fin.read((char*)&myAst[i],sizeof(myAst[0]));
  
  fin.close();
  return myAst;
}
*/
AsteroidWithPole* bin2ast(long *N)
{
  ifstream fin("ast.bin");
  fin.read((char*)N,sizeof(*N));

  AsteroidWithPole *myAst = new AsteroidWithPole[*N];

  for(long i=0; i<*N; i++)
    fin.read((char*)&myAst[i],sizeof(myAst[0]));
  
  fin.close();
  return myAst;
}

AsteroidWithPole* bin2ast(long *N, char* inputFileName)
{
  ifstream fin(inputFileName);
  fin.read((char*)N,sizeof(*N));

  AsteroidWithPole *myAst = new AsteroidWithPole[*N];

  for(long i=0; i<*N; i++)
    fin.read((char*)&myAst[i],sizeof(myAst[0]));
  
  fin.close();
  return myAst;
}

vector <string> decodeTaxInput(char *taxInput, int *Ntaxa)
{
  int lenInput=strlen(taxInput);

  vector <string> taxa;
  char myTax[4];
  int myTaxInd=0;
  
  memset(myTax,'-',3); myTax[3]=0;
  
  for(int i=0; i<=lenInput; i++)
    {
      if ((taxInput[i]==',')||(taxInput[i]==0))
	{
	  taxa.push_back(myTax);
	  Ntaxa[0]++;
	  myTaxInd=0;
	  memset(myTax,'-',3); myTax[3]=0;
	} else {
	myTax[myTaxInd++]=taxInput[i];
      }
    }
  return taxa;
}

int main(int argc, char* argv[])
{
  long N;
  char inputFileName[512]="ast.bin";

  double assumed_pV=0.05;
  double DfromHpV=0;
  double Dlo=-1, Dhi=10000;
  double pVlo=-1, pVhi=10;
  double pAlo=-1, pAhi=100;
  double pElo=-1, pEhi=1.01;
  double hLo=-1, hHi=50;
  double pSinIlo=-1, pSinIhi=1.01;
  double famHI=1000000, famLO=-1;
  double foLO=-1, foHI=100;
  int gnuplotInterface=0;
  char taxInput[80];
  int NtaxaIn=0;
  vector <string> taxaIn;
  int NtaxaOut=0;
  vector <string> taxaOut;
  long numberLO=-2, numberHI=1000000;
  long Number=-1;
  float qLO=-1, qHI=10000.0;
  float Qlo=-1, Qhi=10000.0;
  int recalcAlbedo=0;
  float a0=0;
  float slope=0;
  int outMode=0;
  
  
    //	SCANS the arguments
  while (argc>1)
    {
      if (argv[1][0]=='-')
	{
	  switch (argv[1][1])
	    {
	    case 'V':
	      argc--; argv++; a0=atof(argv[1]);
	      argc--; argv++; slope=atof(argv[1]);
	      break;
	    case 'b':
	      argc--; argv++; strcpy(inputFileName,argv[1]);
	      break;
	    case 'q':
	      argc--; argv++; qLO=atof(argv[1]);
	      argc--; argv++; qHI=atof(argv[1]);	      
	      break;
	    case 'N':
	      argc--; argv++; Number=atol(argv[1]);
	      break;
	    case 'F':
	      argc--; argv++; foLO=atof(argv[1]);
	      argc--; argv++; foHI=atof(argv[1]);	      
	      break;
	    case 'n':
	      argc--; argv++; numberLO=atol(argv[1]);
	      argc--; argv++; numberHI=atol(argv[1]);
	      break;
	    case 'p':
	      argc--; argv++; pVlo=atof(argv[1]);
	      argc--; argv++; pVhi=atof(argv[1]);
	      //	      printf("albedo range is %ld %ld\n",pVlo,pVhi);
	      break;
	    case 'a':
	      argc--; argv++; pAlo=atof(argv[1]);
	      argc--; argv++; pAhi=atof(argv[1]);
	      //	      printf("albedo range is %ld %ld\n",pVlo,pVhi);
	      break;
	    case 'e':
	      argc--; argv++; pElo=atof(argv[1]);
	      argc--; argv++; pEhi=atof(argv[1]);
	      //	      printf("albedo range is %ld %ld\n",pVlo,pVhi);
	      break;
	    case 'i':
	      argc--; argv++; pSinIlo=atof(argv[1]);
	      argc--; argv++; pSinIhi=atof(argv[1]);
	      //	      printf("albedo range is %ld %ld\n",pVlo,pVhi);
	      break;
	    case 'f':
	      argc--; argv++; famLO=atof(argv[1]);
	      argc--; argv++; famHI=atof(argv[1]);
	      //	      printf("albedo range is %ld %ld\n",pVlo,pVhi);
	      break;
	    case 'H':
	      argc--; argv++; hLo=atof(argv[1]);
	      argc--; argv++; hHi=atof(argv[1]);
	      //	      printf("albedo range is %ld %ld\n",pVlo,pVhi);
	      break;
	    case 'D':
	      argc--; argv++; Dlo=atof(argv[1]);
	      argc--; argv++; Dhi=atof(argv[1]);
	      //	      printf("albedo range is %ld %ld\n",pVlo,pVhi);
	      break;
	    case 'G': // activate gnuplotInterface
	      gnuplotInterface=1;
	      //	      printf("albedo range is %ld %ld\n",pVlo,pVhi);
	      break;
	    case 'T':
	      NtaxaIn=0;
	      argc--; argv++; strcpy(taxInput,argv[1]);
	      taxaIn = decodeTaxInput(taxInput, &NtaxaIn);
	      break;
	    case 't':
	      NtaxaOut=0;
	      argc--; argv++; strcpy(taxInput,argv[1]);
	      taxaOut = decodeTaxInput(taxInput, &NtaxaOut);
	      break;
	    case 'Q':
	      argc--; argv++; Qlo=atof(argv[1]);
	      argc--; argv++; Qhi=atof(argv[1]);
	      break;
	    case 'P': // recalc albedos
	      recalcAlbedo=1;
	      break;
	    case 'o':
	      argc--; argv++; outMode=atol(argv[1]);
	      break;
	    default:
	      printf("unknown option -%s\n",&argv[1][1]);
	      return 0;
	      break;
	    } //switch
	}
      argc--; argv++;
    }

  AsteroidWithPole *myAst = bin2ast(&N, inputFileName); // create and load asteroids

  if (recalcAlbedo)
    for (long i=0; i<N; i++)
      myAst[i].HD2pV();

  if (Number>=0)
    {
      myAst[Number-1].print();
      return 0;
    }
  
  if (!gnuplotInterface)
    {
      for(long i=0; i<N; i++)
	{
	  float q=myAst[i].a*(1-myAst[i].e);		  
	  float Q=myAst[i].a*(1+myAst[i].e);		  
	  if ((myAst[i].D>=Dlo) && (myAst[i].D<Dhi) && 
	      (myAst[i].pV>=pVlo) && (myAst[i].pV<pVhi) &&
	      (myAst[i].ap>=pAlo) && (myAst[i].ap<pAhi) &&
	      (myAst[i].ep>=pElo) && (myAst[i].ep<pEhi) &&
	      (myAst[i].H>=hLo) && (myAst[i].H<hHi) &&
	      (myAst[i].Fo>=foLO) && (myAst[i].Fo<foHI) &&
	      (myAst[i].sinip>=pSinIlo) && (myAst[i].sinip<pSinIhi) &&
	      (myAst[i].Number>=numberLO) && (myAst[i].Number<numberHI) &&
	      (myAst[i].familyDavidN>=famLO) && (myAst[i].familyDavidN<famHI) &&
	      (q>=qLO) && (q<qHI) &&
	      (Q>=Qlo) && (Q<Qhi))	      
	    {
	      if (NtaxaIn) // REQUest 4 TAX in.. the object must hasTax
		{
		  if (!myAst[i].hasTax) continue; // has no tax so quit
		  else {
		    if (myAst[i].hasTheseTaxonomies(taxaIn))
		      {
			if (NtaxaOut)
			  {
			    if (myAst[i].hasNotTheseTaxonomies(taxaOut)) myAst[i].print(outMode);
			    else continue;
			  } else
			    myAst[i].print(outMode);
		      } else continue;
		  }
		}
	      if (NtaxaOut) // REQUest 4 TAX Out
		{
		  if (!myAst[i].hasTax) myAst[i].print(); // has no tax so quit
		  else
		    if (myAst[i].hasNotTheseTaxonomies(taxaOut)) myAst[i].print(outMode);
		    else continue;
		}
	      if ((!NtaxaOut)&&(!NtaxaIn))
		{
		  myAst[i].print(outMode);
		  //		  printf(" %06ld\n",i+1);
		}

	    }
	} // for
    }// nogplot
  else // run gnuplot interface
    {
      cout << "preparing vector for gp" << endl;
      vector <AsteroidWithPole> ast;
      for(long i=0; i<N; i++)
	{
	  float q=myAst[i].a*(1-myAst[i].e);		  
	  if ((myAst[i].D>=Dlo) && (myAst[i].D<Dhi) && 
	      (myAst[i].pV>=pVlo) && (myAst[i].pV<pVhi) &&
	      (myAst[i].ap>=pAlo) && (myAst[i].ap<pAhi) &&
	      (myAst[i].ep>=pElo) && (myAst[i].ep<pEhi) &&
	      (myAst[i].H>=hLo) && (myAst[i].H<hHi) &&
	      (myAst[i].Fo>=foLO) && (myAst[i].Fo<foHI) &&
	      (myAst[i].sinip>=pSinIlo) && (myAst[i].sinip<pSinIhi) &&
	      (myAst[i].Number>=numberLO) && (myAst[i].Number<numberHI) &&
	      (myAst[i].familyDavidN>=famLO) && (myAst[i].familyDavidN<famHI) &&
	      (q>=qLO) && (q<qHI))
	    {
	      if (NtaxaIn) // REQUest 4 TAX in.. the object must hasTax
		{
		  if (!myAst[i].hasTax) continue; // has no tax so quit
		  else {
		    if (myAst[i].hasTheseTaxonomies(taxaIn))
		      {
			if (NtaxaOut)
			  {
			    if (myAst[i].hasNotTheseTaxonomies(taxaOut))  ast.push_back(myAst[i]);
			    else continue;
			  } else
			  ast.push_back(myAst[i]);
		      } else continue;
		  }
		}
	      if (NtaxaOut) // REQUest 4 TAX Out
		{
		  if (!myAst[i].hasTax) myAst[i].print(); // has no tax so quit
		  else
		    if (myAst[i].hasNotTheseTaxonomies(taxaOut))  ast.push_back(myAst[i]);
		    else continue;
		}
	      if ((!NtaxaOut)&&(!NtaxaIn))
		ast.push_back(myAst[i]);
	    }
	} // for

      FILE *gpin;
      char buff[512];
      FILE *gp;

      /* Create a FIFO we later use for communication gnuplot => our program. */
      if (mkfifo(GPFIFO, 0600)) {
	if (errno != EEXIST) {
	  perror(GPFIFO);
	  unlink(GPFIFO);
	  return 1;
	}
      }

      if (NULL == (gp = popen("gnuplot","w"))) {
	perror("gnuplot");
	pclose(gp);
	return 1;
      }
      puts("Connected to gnuplot.\n");
      
      /* Init mouse and redirect all gnuplot printings to our FIFO */
      fprintf(gp, "set mouse; set print \"%s\"\n", GPFIFO);
      fflush(gp);
      
      /* Sometimes it was necessary to print \n from gnuplot to avoid a block.
	 Probably it is no more necessary.
      */
#if 0
      fprintf(gp, "print \"\\n\"\n");
      fflush(gp);
#endif

      /* Open the FIFO (where gnuplot is writing to) for reading. */
      if (NULL == (gpin = fopen(GPFIFO,"r"))) {
	perror(GPFIFO);
	pclose(gp);
	return 1;
      }

      puts("FIFO open for reading.\n");
      puts("Click on plot to get nearby asteroids.\n");
      puts("Press any key in THIS window to quit.\n");

      // NOW DO THE WORK

      fprintf(gp, "set cbrange [0:.3]\n");
      // first send valid data to gnuplot for plotting
      //      fprintf(gp, "plot [][0:.2]0.6*(abs(x-2.37)), '-' u 1:2:3 palette pt 7 ps 1\n"); // NOMINAL MARCO'S AF
      //      fprintf(gp, "plot [][0:.2]-0.59*x+1.405, 0.6*(abs(x-2.366)), '-' u 1:2:3 palette pt 7 ps 1\n"); // SpotoBins MARCO'S AF
      //      fprintf(gp, "plot [][0:.2]0.6*(abs(x-2.4)), 0.6*(abs(x-2.366)), '-' u 1:2:3 palette pt 7 ps 1\n"); // Tests
      if ((slope)&&(a0))
	fprintf(gp, "plot [][0:.2]%f*(abs(x-%f)), '-' u 1:2:3 palette pt 7 ps 1\n",slope,a0); // Tests
      else
	fprintf(gp, "plot '-' u 1:2:3 palette pt 7 ps 1\n");
      
      for (long i=0; i<ast.size(); i++)
	//	fprintf(gp,"%lf %lf %lf\n", ast[i].ap, 1./ast[i].D,asin(ast[i].sinip)*180/3.1416);
	fprintf(gp,"%lf %lf %lf\n", ast[i].ap, 1./ast[i].D, ast[i].pV);
      fprintf(gp,"e\n");
      fflush(gp);
      
      double ox=0,oy=0,mx=0,my=0;
      int mb;
      while(!kbhit()) {
	//    printf("\n%i/5. -- Click anywhere in the graph by mouse button 1.\n", n+1);
	fprintf(gp, "pause 1\n");
	fprintf(gp, "if (exists('MOUSE_X')) print MOUSE_X, MOUSE_Y, MOUSE_BUTTON; else print '0 0 0'\n");
	fflush(gp);
	
    //    fprintf(stdout,"I'M HERE: %i\n", __LINE__);
    /* Read from the FIFO. */
    
	fscanf(gpin, "%lf %lf %d", &mx, &my, &mb);
	if ((mx!=ox) && (my!=oy)){
	  //      fprintf(stdout,"I'M HERE: %i\n", __LINE__);
	  //	  printf("You pressed mouse button %d at x=%f y=%f\n", mb, mx, my);
	  ox=mx;
	  oy=my;

	  double d, dMin=1000;
	  long iMin=1;
	  // now scans for closest object
	  for(long i = 0; i < ast.size(); i++)
	    {
	      d=(ast[i].ap-mx)*(ast[i].ap-mx)+(1/ast[i].D-my)*(1/ast[i].D-my);
	      if (d<dMin)
		{
		  dMin=d;
		  iMin=i;
		}
	    }
	  if ((iMin>-1)&&(iMin<ast.size()))
	      ast[iMin].print();
	}
      }
      fprintf(gp, "q\n", GPFIFO);
      fflush(gp);
      fclose(gpin);
      pclose(gp);
      unlink(GPFIFO);
      puts("FIFO Pipe closed Gnuplot CLosed\n\n");
    }
}
